<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no, viewport-fit=cover"/>
<meta name="description" content="Data flow or workflow graph of nodes with varying input and output ports with labels, oriented vertically."/> 
<link rel="stylesheet" href="../assets/css/style.css"/> 
<!-- Copyright 1998-2021 by Northwoods Software Corporation. -->
<title>Data Flow Diagram</title>
</head>

<body>
  <!-- This top nav is not part of the sample code -->
  <nav id="navTop" class="w-full z-30 top-0 text-white bg-nwoods-primary">
    <div class="w-full container max-w-screen-lg mx-auto flex flex-wrap sm:flex-nowrap items-center justify-between mt-0 py-2">
      <div class="md:pl-4">
        <a class="text-white hover:text-white no-underline hover:no-underline
        font-bold text-2xl lg:text-4xl rounded-lg hover:bg-nwoods-secondary " href="../">
          <h1 class="mb-0 p-1 ">GoJS</h1>
        </a>
      </div>
      <button id="topnavButton" class="rounded-lg sm:hidden focus:outline-none focus:ring" aria-label="Navigation">
        <svg fill="currentColor" viewBox="0 0 20 20" class="w-6 h-6">
          <path id="topnavOpen" fill-rule="evenodd" d="M3 5a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 10a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM9 15a1 1 0 011-1h6a1 1 0 110 2h-6a1 1 0 01-1-1z" clip-rule="evenodd"></path>
          <path id="topnavClosed" class="hidden" fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd"></path>
        </svg>
      </button>
      <div id="topnavList" class="hidden lg:text-base sm:block items-center w-auto mt-0 text-white p-0 z-20">
        <ul class="list-reset list-none font-semibold flex justify-end flex-wrap sm:flex-nowrap items-center px-0 pb-0">
          <li class="p-1 sm:p-0"><a class="topnav-link" href="../learn/">Learn</a></li>
          <li class="p-1 sm:p-0"><a class="topnav-link" href="../samples/">Samples</a></li>
          <li class="p-1 sm:p-0"><a class="topnav-link" href="../intro/">Intro</a></li>
          <li class="p-1 sm:p-0"><a class="topnav-link" href="../api/">API</a></li>
          <li class="p-1 sm:p-0"><a class="topnav-link" href="https://www.nwoods.com/products/register.html">Register</a></li>
          <li class="p-1 sm:p-0"><a class="topnav-link" href="../download.html">Download</a></li>
          <li class="p-1 sm:p-0"><a class="topnav-link" href="https://forum.nwoods.com/c/gojs/11">Forum</a></li>
          <li class="p-1 sm:p-0"><a class="topnav-link" href="https://www.nwoods.com/contact.html"
           target="_blank" rel="noopener" onclick="getOutboundLink('https://www.nwoods.com/contact.html', 'contact');">Contact</a></li>
          <li class="p-1 sm:p-0"><a class="topnav-link" href="https://www.nwoods.com/sales/index.html"
           target="_blank" rel="noopener" onclick="getOutboundLink('https://www.nwoods.com/sales/index.html', 'buy');">Buy</a></li>
        </ul>
      </div>
    </div>
    <hr class="border-b border-gray-600 opacity-50 my-0 py-0" />
  </nav>
  <div class="md:flex flex-col md:flex-row md:min-h-screen w-full max-w-screen-xl mx-auto">
    <div id="navSide" class="flex flex-col w-full md:w-48 text-gray-700 bg-white flex-shrink-0"></div>
    <!-- * * * * * * * * * * * * * -->
    <!-- Start of GoJS sample code -->
    
    <script src="../release/go.js"></script>
    <div class="p-4 w-full">
    <script id="code">
    function init() {
      var $ = go.GraphObject.make;

      myDiagram =
        $(go.Diagram, "myDiagramDiv",
          {
            initialContentAlignment: go.Spot.Top,
            initialAutoScale: go.Diagram.UniformToFill,
            layout: $(go.LayeredDigraphLayout,
              { direction: 90 }),
            "undoManager.isEnabled": true
          }
        );

      // when the document is modified, add a "*" to the title and enable the "Save" button
      myDiagram.addDiagramListener("Modified", function(e) {
        var button = document.getElementById("SaveButton");
        if (button) button.disabled = !myDiagram.isModified;
        var idx = document.title.indexOf("*");
        if (myDiagram.isModified) {
          if (idx < 0) document.title += "*";
        } else {
          if (idx >= 0) document.title = document.title.substr(0, idx);
        }
      });

      // when the diagram is vertically oriented, "left" means "top" and "right" means "bottom"
      function makePort(name, leftside) {
        var port = $(go.Shape, "Circle",
          {
            fill: "black", stroke: null,
            desiredSize: new go.Size(8, 8),
            portId: name,  // declare this object to be a "port"
            toMaxLinks: 1,  // don't allow more than one link into a port
            cursor: "pointer"  // show a different cursor to indicate potential link point
          });

        var lab = $(go.TextBlock, name,  // the name of the port
          { font: "7pt sans-serif" });

        var panel = $(go.Panel, "Vertical",
          { margin: new go.Margin(0, 2) });

        if (leftside) {
          port.toSpot = go.Spot.Top;
          port.toLinkable = true;
          lab.margin = new go.Margin(1, 0, 0, 1);
          panel.alignment = go.Spot.TopLeft;
          panel.add(port);
          panel.add(lab);
        } else {
          port.fromSpot = go.Spot.Bottom;
          port.fromLinkable = true;
          lab.margin = new go.Margin(1, 1, 0, 0);
          panel.alignment = go.Spot.TopRight;
          panel.add(lab);
          panel.add(port);
        }
        return panel;
      }

      function makeTemplate(typename, icon, background, inports, outports) {
        var node = $(go.Node, "Spot",
          $(go.Panel, "Auto",
            { width: 200, height: 90 },
            $(go.Shape, "RoundedRectangle",
              {
                fill: background,
                spot1: go.Spot.TopLeft, spot2: go.Spot.BottomRight
              }),
            $(go.Panel, "Table",
              $(go.TextBlock, typename,
                {
                  column: 0,
                  margin: 3,
                  maxSize: new go.Size(80, NaN),
                  stroke: "black",
                  font: "bold 10pt sans-serif"
                }),
              $(go.Picture, icon,
                { column: 1, width: 55, height: 55 }),
              $(go.TextBlock,
                {
                  column: 2,
                  margin: 3,
                  editable: true,
                  maxSize: new go.Size(80, 40),
                  stroke: "black",
                  font: "bold 9pt sans-serif"
                },
                new go.Binding("text", "name").makeTwoWay())
            )
          ),
          $(go.Panel, "Horizontal",
            {
              alignment: go.Spot.Top,
              alignmentFocus: new go.Spot(0.5, 0, 0, 4)
            },
            inports),
          $(go.Panel, "Horizontal",
            {
              alignment: go.Spot.Bottom,
              alignmentFocus: new go.Spot(0.5, 1, 0, -4)
            },
            outports)
        );
        myDiagram.nodeTemplateMap.set(typename, node);
      }

      makeTemplate("Table", "images/table.svg", "forestgreen",
        [],
        [makePort("OUT", false)]);

      makeTemplate("Join", "images/join.svg", "mediumorchid",
        [makePort("L", true), makePort("R", true)],
        [makePort("UL", false), makePort("ML", false), makePort("M", false), makePort("MR", false), makePort("UR", false)]);

      makeTemplate("Project", "images/project.svg", "darkcyan",
        [makePort("", true)],
        [makePort("OUT", false)]);

      makeTemplate("Filter", "images/filter.svg", "cornflowerblue",
        [makePort("", true)],
        [makePort("OUT", false), makePort("INV", false)]);

      makeTemplate("Group", "images/group.svg", "mediumpurple",
        [makePort("", true)],
        [makePort("OUT", false)]);

      makeTemplate("Sort", "images/sort.svg", "sienna",
        [makePort("", true)],
        [makePort("OUT", false)]);

      makeTemplate("Export", "images/upload.svg", "darkred",
        [makePort("", true)],
        []);

      myDiagram.linkTemplate =
        $(go.Link,
          {
            routing: go.Link.Orthogonal, corner: 5,
            relinkableFrom: true, relinkableTo: true
          },
          $(go.Shape, { stroke: "black", strokeWidth: 2 })
        );

      load();
    }

    // Show the diagram's model in JSON format that the user may edit
    function save() {
      document.getElementById("mySavedModel").value = myDiagram.model.toJson();
      myDiagram.isModified = false;
    }
    function load() {
      myDiagram.model = go.Model.fromJson(document.getElementById("mySavedModel").value);
    }
    window.addEventListener('DOMContentLoaded', init);
  </script>

<div id="sample">
  <div id="myDiagramDiv" style="border: solid 1px black; width: 100%; height: 600px"></div>
  <p>
    This sample demonstrates a data flow or workflow graph with labeled ports on nodes. These ports are set up as panels, created within
    the <b>makePort</b> function. This function sets various properties of the <a>Shape</a> and
    <a>TextBlock</a> that make up the panel, and properties of the panel itself. Most notable are
    <a>GraphObject.portId</a> to declare the shape as a port, and <a>GraphObject.fromLinkable</a> and
    <a>GraphObject.toLinkable</a> to set the way the ports can be linked.
  </p>
  <p>
    The diagram also uses the <b>makeTemplate</b> function to create the node templates with shared features.
    This function takes a type, an image, a background color, and arrays of ports to create the node
    to be added to the <a>Diagram.nodeTemplateMap</a>.
  </p>
  <p>
    For the same data model rendered somewhat differently, see the <a href="dataFlow.html">Data Flow (horizontal)</a> sample.
  </p>
  <div>
    <div>
      <button id="SaveButton" onclick="save()">Save</button>
      <button onclick="load()">Load</button>
      Diagram Model saved in JSON format:
    </div>
    <textarea id="mySavedModel" style="width:100%;height:300px">
{ "class": "go.GraphLinksModel",
  "nodeCategoryProperty": "type",
  "linkFromPortIdProperty": "frompid",
  "linkToPortIdProperty": "topid",
  "nodeDataArray": [
{"key":1, "type":"Table", "name":"Product"},
{"key":2, "type":"Table", "name":"Sales"},
{"key":3, "type":"Table", "name":"Period"},
{"key":4, "type":"Table", "name":"Store"},
{"key":11, "type":"Join", "name":"Product, Class"},
{"key":12, "type":"Join", "name":"Period"},
{"key":13, "type":"Join", "name":"Store"},
{"key":21, "type":"Project", "name":"Product, Class"},
{"key":31, "type":"Filter", "name":"Boston, Jan2014"},
{"key":32, "type":"Filter", "name":"Boston, 2014"},
{"key":41, "type":"Group", "name":"Sales"},
{"key":42, "type":"Group", "name":"Total Sales"},
{"key":51, "type":"Join", "name":"Product Name"},
{"key":61, "type":"Sort", "name":"Product Name"},
{"key":71, "type":"Export", "name":"File"}
  ],
  "linkDataArray": [
{"from":1, "frompid":"OUT", "to":11, "topid":"L"},
{"from":2, "frompid":"OUT", "to":11, "topid":"R"},
{"from":3, "frompid":"OUT", "to":12, "topid":"R"},
{"from":4, "frompid":"OUT", "to":13, "topid":"R"},
{"from":11, "frompid":"M", "to":12, "topid":"L"},
{"from":12, "frompid":"M", "to":13, "topid":"L"},
{"from":13, "frompid":"M", "to":21},
{"from":21, "frompid":"OUT", "to":31},
{"from":21, "frompid":"OUT", "to":32},
{"from":31, "frompid":"OUT", "to":41},
{"from":32, "frompid":"OUT", "to":42},
{"from":41, "frompid":"OUT", "to":51, "topid":"L"},
{"from":42, "frompid":"OUT", "to":51, "topid":"R"},
{"from":51, "frompid":"OUT", "to":61},
{"from":61, "frompid":"OUT", "to":71}
  ]}
    </textarea>
  </div>
</div>
    </div>
    <!-- * * * * * * * * * * * * * -->
    <!--  End of GoJS sample code  -->
  </div>
</body>
<!--  This script is part of the gojs.net website, and is not needed to run the sample -->
<script src="../assets/js/goSamples.js"></script>
</html>
